/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 用常量定义 promise 三种状态, 能有提示
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    constructor(executor) {
        // 捕获执行器(executor)代码执行异常时的错误
        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    // status 存储 promise 状态
    status = PENDING;
    // value 存成功的值
    value = undefined;
    // reason 存失败提示
    reason = undefined;
    // 为异步调用，存储成功回调。（数组形式为多次调用 .then 方法时依次存储其回调函数）
    successCallback = [];
    // 为异步调用，存储失败回调。（数组形式为多次调用 .then 方法时依次存储其回调函数）
    failCallback = [];

    // 定义 resolve 成功方法
    resolve = (value) => {
        // 判断 promise 状态，pending 时继续执行
        if (this.status !== PENDING) return;
        // 修改 premiere 状态为成功，用于后续判断
        this.status = FULFILLED;
        // 保存成功的值
        this.value = value;
        // 循环调用多次.then的回调函数，直到清空所有的回调函数
        while (this.successCallback.length) {
            return this.successCallback.shift()();
        }
    };

    // 定义 reject 失败方法
    reject = (reason) => {
        // 判断 promise 状态，pending 时继续执行
        if (this.status !== PENDING) return;
        // 修改 premiere 状态为成功，用于后续判断
        this.status = REJECTED;
        // 保存成功的值
        this.reason = reason;
        // 循环调用多次.then的回调函数，直到清空所有的回调函数
        while (this.failCallback.length) {
            return this.failCallback.shift()();
        }
    };

    // 定义 then 方法
    then(successCallback, failCallback) {
        // then 方法参数可选
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback : reason => { throw reason };

        // then 方法返回一个promise对象
        let thenPromise = new MyPromise((resolve, reject) => {
            // 判断 promise 状态
            if (this.status === FULFILLED) {
                // 因为new Promise需要执行完成之后才有thenPromise，同步代码中没有thenPromise
                // 使用异步操作拿到 thenPromise
                setTimeout(() => {
                    // 如果回调中报错的话就执行reject
                    try {
                        // 创建一个变量接收 successCallback的返回值
                        let callbackValue = successCallback(this.value)

                        // 调用 resolvePromise 判断 callbackValue 的值是 promise对象 还是 其他值
                        // 如果是promise对象 查看 promsie对象 返回的结果，再根据 promise对象 返回的结果 决定调用resolve 还是调用reject
                        // 如果是其他值 则调用 resolve() 返回
                        // 需要判断then之后return的promise对象和原来的是不是一样的，判断 callbackValue 和 thenPromise 是否相等
                        // 所以要给 resolvePromise 传递 4个参数，thenPromise，callbackValue， resolve， reject
                        resolvePromise(thenPromise, callbackValue, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    // 如果回调中报错的话就执行reject
                    try {
                        // 创建一个变量接收 failCallback的返回值
                        let callbackValue = failCallback(this.reason)

                        // 调用 resolvePromise 判断 callbackValue 的值是 promise对象 还是 其他值
                        // 如果是promise对象 查看 promsie对象 返回的结果，再根据 promise对象 返回的结果 决定调用resolve 还是调用reject
                        // 如果是其他值 则调用 resolve() 返回
                        // 需要判断then之后return的promise对象和原来的是不是一样的，判断 callbackValue 和 thenPromise 是否相等
                        // 所以要给 resolvePromise 传递 4个参数，thenPromise，callbackValue， resolve， reject
                        resolvePromise(thenPromise, callbackValue, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else {
                // 异步情况，promise 进入等待，状态不变
                // 将成功、失败的回调存起来
                this.successCallback.push(() => {
                    setTimeout(() => {
                        // 处理异步的成功错误情况
                        // 如果回调中报错的话就执行reject
                        try {
                            // 创建一个变量接收 failCallback的返回值
                            let callbackValue = successCallback(this.value)

                            // 调用 resolvePromise 判断 callbackValue 的值是 promise对象 还是 其他值
                            // 如果是promise对象 查看 promsie对象 返回的结果，再根据 promise对象 返回的结果 决定调用resolve 还是调用reject
                            // 如果是其他值 则调用 resolve() 返回
                            // 需要判断then之后return的promise对象和原来的是不是一样的，判断 callbackValue 和 thenPromise 是否相等
                            // 所以要给 resolvePromise 传递 4个参数，thenPromise，callbackValue， resolve， reject
                            resolvePromise(thenPromise, callbackValue, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        // 处理异步的成功错误情况
                        // 如果回调中报错的话就执行reject
                        try {
                            // 创建一个变量接收 successCallback的返回值
                            let callbackValue = failCallback(this.reason)

                            // 调用 resolvePromise 判断 callbackValue 的值是 promise对象 还是 其他值
                            // 如果是promise对象 查看 promsie对象 返回的结果，再根据 promise对象 返回的结果 决定调用resolve 还是调用reject
                            // 如果是其他值 则调用 resolve() 返回
                            // 需要判断then之后return的promise对象和原来的是不是一样的，判断 callbackValue 和 thenPromise 是否相等
                            // 所以要给 resolvePromise 传递 4个参数，thenPromise，callbackValue， resolve， reject
                            resolvePromise(thenPromise, callbackValue, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })
            }
        })
        return thenPromise
    }

    // 定义原型对象上的 finally 方法
    finally(callback) {
        // 不论是否成功都要调用 callback函数
        // 所以调用 then 方法获取 promise 状态，并返回其结果
        return this.then(
            (value) => {
                // 如果callback是一个异步的promise对象，我们还需要等待其执行完毕，
                // 需要用到静态方法resolve，把callback()调用之后返回的promise对象传递过去，并且执行promise，且在成功之后返回value
                return MyPromise.resolve(callback()).then(() => value)
            },
            // 失败之后调用的then方法，然后把失败的原因返回出去。
            (reason) => {
                return MyPromise.resolve(callback().then(() => { throw reason }))
            }
        )
    }

    // 定义 catch 方法
    // 只接收错误回调
    catch (failCallback) {
        // 在内部调用 then 方法，成功回调位置传 undefined
        return this.then(undefined, failCallback)
    }

    // 静态方法 all
    static all(array) {
        // 创建一个数组接收结果
        let result = [];
        // 创建一个计数器，确保结果数组的个数与接收的一致
        let num = 0
        // all 返回一个promise对象
        return new MyPromise((resolve, reject) => {
            // 创建一个方法，将所有的成功返回值都放入 result 数组
            function addItem(index, value) {
                result[index] = value
                num++
                // 当计数器与接收的数组长度一致时，证明一件处理完毕，输出结果数组result
                if (num === array.length) {
                    resolve(result)
                }
            }
            // 遍历接收数组的每一项
            for (let i = 0; i < array.length; i++) {
                let item = array[i]
                // 判断当前项是否为 promise 对象
                if (item instanceof MyPromise) {
                    // 如果是 promise对象，则调用 then 方法处理取得结果
                    item.then((value) => addItem(i, value), (reason) => reject(reason))
                } else {
                    // 如果为其他值则直接加入结果数组
                    addItem(i, item)
                }
            }
        })
    }

    // 创建 resolve 静态方法
    static resolve(value) {
        // resolve 方法返回 promise对象
        // 判断接收的值时否为 promise对象，是则直接返回，不是则将接收值放入promise中返回
        if (value instanceof MyPromise) {
            return value
        }
        return new MyPromise((resolve) => resolve(value))
    }
}

// 因为Mypromise 在成功、失败、异步都需要处理 callBack 返回值，
// 所以创建一个 resolvePromise 专门处理 callbackValue 相关的问题
function resolvePromise(thenPromise, callbackValue, resolve, reject) {
    // 需要判断then之后return的promise对象和原来的是不是一样的，判断 callbackValue 和 thenPromise 是否相等
    if (thenPromise === callbackValue) {
        // 如果相等了，说明return的是自己，抛出类型错误并返回
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    // 判断succValue 是否为 MyPromise 的实例对象
    if (callbackValue instanceof MyPromise) {
        // 如果是 promise 对象
        callbackValue.then(value => resolve(value), reason => reject(reason));
        // 可简写为下式
        // callbackValue.then(resolve, reject)
    } else {
        // 如果是其他值resolve返回
        resolve(callbackValue)
    }
}

module.exports = MyPromise;
